package dmir.wikipedia;


import java.io.Closeable;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.DecimalFormat;
import java.util.List;

import org.xml.sax.Attributes;
import org.xml.sax.helpers.DefaultHandler;

/**
 * Parser for the Wikipedia XML dump file. 
 * 
 * @author ivo
 *
 */
public class WikiPageParser extends DefaultHandler implements Closeable{

    private static final String PAGE = "page";
    private static final String TITLE = "title";
    private static final String TEXT = "text";
    private static final String NOTAG = "";
    
    private FileOutputStream graph;
    private FileOutputStream content;
    private FileOutputStream categories;
    private FileOutputStream linksEN;
    private FileOutputStream coordinates;
    private FileOutputStream population;
    private FileOutputStream area;
    
    private String currentTag;

    private final StringBuilder currentWikitext = new StringBuilder();
    private final StringBuilder currentTitle = new StringBuilder();
    private final String language;
    
    private static final DecimalFormat COORDINATES_FORMAT = new DecimalFormat("0.0####");
    
    public WikiPageParser(
    		String graph, 
    		String content,
    		String categories,
    		String linksEN, 
    		String coordinates,
    		String population,
    		String area,
    		String lang) throws Exception {
    	
    	this.language = lang;
        
        if (graph != null) this.graph = new FileOutputStream(graph);
        if (content != null) this.content = new FileOutputStream(content);
        if (linksEN != null) this.linksEN = new FileOutputStream(linksEN);
        if (categories != null) this.categories = new FileOutputStream(categories);
        if (coordinates != null) this.coordinates = new FileOutputStream(coordinates);
        if (population != null) this.population = new FileOutputStream(population);
        if (area != null) this.area = new FileOutputStream(area);
    }
    
    /**
     * Close all output streams.
     * 
     */
    public void close() {
    	try {
            if (graph != null) this.graph.close();
            if (content != null) this.content.close();
            if (linksEN != null) this.linksEN.close();
            if (categories != null) this.categories.close();
            if (coordinates != null) this.coordinates.close();
            if (population != null) this.population.close();
            if (area != null) this.area.close();
    	} catch (IOException e) {
    		e.printStackTrace();
    	}
    }
    
    /**
     * Takes the information obtained from the Wikipedia article that was just obtained by the SAX
     * parser and writes it to disk.
     * 
     * @param page
     */
    public void write(WikiPage page) {

        try {
            if (page.ignore()) return;
            
            String text = page.getPlainText();
            byte[] source = page.getTitle().getBytes();
            List<WikiLink> links = page.getLinks();

            if (page.isRedirect()) {

                if (graph != null && links.size() > 0) {
                    graph.write('r');
                    graph.write('\t');
                    graph.write(source);
                    graph.write('\t');
                    graph.write(links.get(0).getAnchorTarget().getBytes());
                    graph.write('\n');
                }

            } else if (page.isDisambiguation()) {

                if (graph != null) {
                    for (int i = 0; i < links.size(); i++) {
                    	
                    	if (links.get(i).getAnchorTarget().toLowerCase().startsWith("en:")) 
                    		continue;
                    	
                        graph.write('d');
                        graph.write('\t');
                        graph.write(source);
                        graph.write('\t');
                        graph.write(links.get(i).getAnchorTarget().getBytes());
                        graph.write('\n');
                    }
                }

            } else {

            	if (graph != null) {
            		for (int i = 0; i < links.size(); i++) 
            			if (!links.get(i).getAnchorTarget().toLowerCase().startsWith("en:")) {
            				graph.write('i');
            				graph.write('\t');
            				graph.write(source);
            				graph.write('\t');
            				graph.write(links.get(i).getAnchorTarget().getBytes());
            				graph.write('\t');
            				graph.write(links.get(i).getAnchorText().getBytes());
            				graph.write('\n');
            			} else if (linksEN != null) {
            				linksEN.write(source);
            				linksEN.write('\t');
            				linksEN.write(links.get(i).getAnchorTarget().substring(3).getBytes());
            				linksEN.write('\n');
            			}
            	}

                if (content != null) {
                    content.write(source);
                    content.write('\t');
                    content.write(text.getBytes());
                    content.write('\n');
                }
                
                if (categories != null) {
                    categories.write(source);
                    for (String c : page.getCategories()) {
                        categories.write('\t');
                        categories.write( c.getBytes() );
                    }
                    categories.write('\n');
                }
                
                if (coordinates != null) {
                    Double[] latLng = page.getLatLng();
                    if (latLng[0] != null && latLng[1] != null) {

                        coordinates.write(source);
                        coordinates.write('\t');
                        coordinates.write(COORDINATES_FORMAT.format(latLng[0]).getBytes());
                        coordinates.write('\t');
                        coordinates.write(COORDINATES_FORMAT.format(latLng[1]).getBytes());
                        coordinates.write('\n');
                    }
                }
                
                if (population != null) {
                    Long pop = page.getPopulation();
                    if (pop != null) 
                    {
                    	population.write(source);
                    	population.write('\t');
                    	population.write(pop.toString().getBytes());
                    	population.write('\n');
                    }
                }
            	
                if (area != null) {
                    Float areaSize = page.getArea();
                    if (areaSize != null) 
                    {
                    	area.write(source);
                    	area.write('\t');
                    	area.write(areaSize.toString().getBytes());
                    	area.write('\n');
                    }
                }
            }                    
        } catch (IOException e) {
            e.printStackTrace();
            System.err.println(page.getTitle());
        }
    }
    
    
    ////////////////////////////////
    /////////// SAX Parser methods
    ////////////////////////////////
    
    
    public void startElement(String uri, String name, String qName, Attributes attr){
        
        currentTag = qName;
        
        if (qName.equals(PAGE)){
            currentWikitext.setLength(0);
            currentTitle.setLength(0);
        }
    }
    
    
    public void endElement(String uri, String name, String qName){
        
        if (qName.equals(PAGE)) { 
            WikiPage currentPage = new WikiPage(currentTitle, currentWikitext, language);
            write(currentPage);
        }
        
        currentTag = NOTAG;
    }
    
    
    public void characters(char ch[], int start, int length){
        
        if (currentTag.equals(TITLE)){
            
            currentTitle.append(ch, start, length);
            
        } else if (currentTag.equals(TEXT)){
            
            currentWikitext.append(ch, start, length);
        }
    }
}
